/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package wiView.graphics.Drawer.GraphAnimation;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import org.graphstream.ui.spriteManager.Sprite;
import org.graphstream.ui.spriteManager.SpriteManager;
import wiEngine.WiEngine;
import wiView.graphics.GraphObject.EdgeData;
import wiView.graphics.GraphObject.WiGraph;
import wiView.graphics.GraphObject.wiEdge;
import wiView.graphics.GraphObject.wiNode;

/**
 *
 * @author Hoang
 */
public class AnimationHandle extends AbstractAnimationHandle {

    WiEngine engine;
    SpriteManager spriteMan;
    WiGraph graph;
    ConcurrentHashMap<String, Animator> animators;
    Clock clock;
    int colorNumber = 4;
    int maxSpritePerEdge = 5;
    int speed = 1;

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
        clock.setSpeed(speed);
    }

    @Override
    public void Stop() {
        super.Stop();
        clock.Stop();
        
        clearNodeAnimation();
        drawingNodes.clear();
        for (String animator : animators.keySet()) {
            tempAnimator.add(animator);
        }
        clearRawData();
    }

    class Clock extends TimerTask {

        Timer timer;
        long time;
        int speed = 1;

        public Clock() {
            timer = new Timer();
            time = 0;

        }

        public void Run() {
            timer.scheduleAtFixedRate(this, 0, speed);
        }

        public void setSpeed(int s){
            if(speed != s){
                speed = s;
            }
        }
        public void Pause() {
            timer.cancel();
        }

        public void Reset() {
            timer.cancel();
            time = 0;
            speed = 1;
            Run();
        }
        public void Stop(){
            timer.purge();
        }
        public long getTime() {
            return time;
        }

        @Override
        public void run() {
            if (engine.isPlay()) {
                time++;
                //long currentTime=System.currentTimeMillis();
            }
        }
    }

    public AnimationHandle(WiGraph g, WiEngine e) {
        graph = g;
        engine = e;
        spriteMan = g.getSpriteMan();
        clock = new Clock();
        animators = new ConcurrentHashMap<>();
    }
    public void setColorNumber(int colorNumber) {
        this.colorNumber = colorNumber;
    }

    @Override
    public void Start(){
        if (engine == null || graph == null) {
            System.out.println("null graph or engine");
            return;
        }
        clock.Run();
        lock = false;
        super.Start();
    }

    @Override
    public void addAnimator(ArrayList<Animator> ani) {
        super.addAnimator(ani);
    }

    @Override
    protected void InitBeforeAdding(Animator ani) {
        if (ani instanceof EdgeAnimator) {
            EdgeAnimator a = (EdgeAnimator) ani;
            int num = Integer.parseInt(a.packetID);
            setColor((EdgeAnimator) ani, num);
        }
    }

    @Override
    protected void _addAnimator(Animator ani) {
        animators.put(ani.id, ani);
    }

    String BuildEdgeID(String node0, String node1) {
        return node0 + "-" + node1;
    }
    HashSet<String> drawingNodes = new HashSet<>();
    HashSet<String> tmp_drawingNodes = new HashSet<>();
    HashSet<String> tempAnimator = new HashSet<>();
    int tick = 1;
    
    @Override
    protected void Repaint() {
        tmp_drawingNodes.addAll(drawingNodes);
        drawingNodes.clear();
        for (Animator ani : animators.values()) {
            if(ani instanceof NodeAnimator){
                if(drawNodeAnimation((NodeAnimator) ani))
                    tempAnimator.add(ani.id);
            }
            else if(ani instanceof EdgeAnimator){
                if(drawEdgeAnimation((EdgeAnimator) ani))
                {
                    tempAnimator.add(ani.id);
                    EdgeAnimator ea = (EdgeAnimator) ani;
                    wiNode n = graph.getwiNode(ea.n1);
                    if (n == null) {
                        
                    }
                    else{
                        n.setState(wiNode.State.Receive);
                        if(drawingNodes.contains(ea.n1))
                            drawingNodes.remove(ea.id);
                        tmp_drawingNodes.add(ea.n1);
                    }
                }
            }
        }
        
        clearNodeAnimation();
        clearRawData();
    }

    boolean drawMessage(String edgeId, String msgId, 
            String color, boolean isNew, double pos){
        wiEdge edge = graph.getwiEdge(edgeId);
        //EdgeData ed = edge.getData();
        try {
            if (isNew) {
                if(spriteMan.hasSprite(msgId))
                    return true;
                
                Sprite s = spriteMan.addSprite(msgId);
                s.attachToEdge(edgeId);
                s.setPosition(0.0);
                s.setAttribute("ui.class", "send, blue, red, brown, grey");
                s.changeAttribute("ui.class", color);

                return false;
            } else {
                Sprite s = spriteMan.getSprite(msgId);
                if (pos > 1) {
                    return true;
                } else {
                    s.setPosition(pos);
                    return false;
                }
            }
        } catch (Exception ex){
            System.out.println("AH: can not draw message: " + ex.getMessage());
            return true;
        }
//        if (pos > 1) {
//            if (ed.spriteCount > 0) {
//                ed.spriteCount--;
//            }
//            return true;
//        }
//        // pos < 1
//        if (ed.spriteCount < maxSpritePerEdge) {
//            Sprite s = spriteMan.addSprite(msgId);
//            s.attachToEdge(edgeId);
//            s.setPosition(pos);
//            s.setAttribute("ui.class", "send, blue, red, brown, grey");
//            s.changeAttribute("ui.class", color);
//            ed.spriteCount++;
//        }
//        return false;
    }
    boolean drawEdgeAnimation(EdgeAnimator a){
        String edgeId = BuildEdgeID(a.n0, a.n1);
        wiEdge edge = graph.getwiEdge(edgeId);
        if (edge == null) {
            edge = graph.addwiEdge(edgeId,
                    a.n0, a.n1);
        }
        switch(a.type){
            case BROAD_CAST:
                // Tam thoi ko co edge animation kieu Broadcast
                break;
            case SEND:
            case FORWARD:
                double pos = 0;// plus pos
                boolean isNew = false;
                if (a.timeStart == -1) {
                    a.timeStart = clock.getTime();
                    isNew = true;
                } 
                else 
                {
                    pos = (clock.getTime() - a.timeStart) * a.speed / engine.getSpeed();
                }
                return drawMessage(edge.getId(), a.id, 
                        a.css, isNew, pos);
            default:
                break;
        }
        return true;
    }

    boolean drawNodeAnimation(NodeAnimator a) {
        wiNode n = graph.getwiNode(a.NodeID);
        if (n == null) {
            return true;
        }

        if (tmp_drawingNodes.contains(a.NodeID)) {
            tmp_drawingNodes.remove(a.NodeID);
        }

        try{
        switch (a.type) {
            case Sleep:
                n.setState(wiNode.State.Sleep);
                return true;
            case Wake:
                n.setState(wiNode.State.Wake);
                return true;
            case Move:
                return true;
            case Broadcast:
                n.setState(wiNode.State.Broadcast);
                break;
            case Send:
                n.setState(wiNode.State.Send);
                break;
            case Forward:
                n.setState(wiNode.State.Forward);
                break;
            case Receive:
                n.setState(wiNode.State.Receive);
                break;
            default : break;
        }
        drawingNodes.add(a.NodeID);
        }
        catch(Exception ex){
            System.out.println("AH: " + ex.getMessage());
        }
        finally{
            return true;
        }
    }
    void clearNodeAnimation(){
        for (String id : tmp_drawingNodes) {
            graph.getwiNode(id).setState(wiNode.State.Wake);
        }
        
        tmp_drawingNodes.clear();
    }
    void clearRawData(){
        for (String animatorId : tempAnimator) {
            Animator a = animators.remove(animatorId);
            if(a instanceof EdgeAnimator){
                String id = a.id;
                if (spriteMan.hasSprite(id)) {
                    spriteMan.removeSprite(id);
                }
            }
            
        }
        tempAnimator.clear();
    }

    @Override
    protected boolean canRun() {
        return engine.isPlay();
    }

    @Override
    protected boolean isContinue() {
        if(engine.isPlay())
            return true;
        if(engine.isStop())
            return false;
        else
            return true;
    }
    
    protected void setColor(EdgeAnimator a, int num) {
        int colorId = num % colorNumber;
        switch (colorId) {
            case 0:
                a.css = "blue";
                break;
            case 1:
                a.css = "black";
                break;
            case 2:
                a.css = "red";
                break;
            case 3:
                a.css = "grey";
                break;
            default:
                break;
        }
    }
    public void clean(){
        animators.clear();
        tempAnimator.clear();
        tmp_drawingNodes.clear();
        drawingNodes.clear();
    }
}
